<template>
  <div class="container-fluid px-0">
    <div class="d-flex flex-column align-items-center justify-content-center">
      <div class="p-3 w-100">
        <select class="form-control" v-model="deviceId">
          <option value="" disabled>-- 选择相机 --</option>
          <option
            v-for="camera in cameras"
            :key="camera.deviceId"
            :value="camera.deviceId"
          >
            {{ camera.label }}
          </option>
        </select>
      </div>
      <div class="input-group mb-3 w-50">
        <span class="input-group-text username_label">姓名</span>
        <input
          type="text"
          class="form-control username"
          placeholder="输入姓名..."
          aria-label="Username"
          aria-describedby="username_label"
          ref="usernameRef"
        />
      </div>
      <div class="p-3 text-danger" v-if="errorMessage">{{ errorMessage }}</div>
      <div class="p-3 text-danger" v-if="!errorMessage">
        请在人脸检测框为绿色时进行提取！
      </div>
      <div class="mx-3 overlay d-flex align-items-center justify-content-center">
        <WebCam
          id="webcam"
          ref="webcam"
          width="100%"
          height="100%"
          :deviceId="deviceId"
          @cameras="onCameras"
          @camera-change="onCameraChange"
          @error="onError"
          @notsupported="onError"
          @video-live="onVideoLive"
        />
      </div>
      <div class="px-3">
        <button
          class="btn btn-primary my-3 d-flex align-items-center justify-content-center"
          @click.prevent="onCapture"
        >
          <span>提取人脸特征</span>
          <i class="fas fa-spinner fa-spin text-white ml-3" v-if="spinner"></i>
        </button>
      </div>
    </div>
  </div>
</template>

<script setup>
  import { getCurrentInstance, onMounted } from 'vue'
  import { useMainStore } from "stores";
  import delay from 'utils/delay';
  import * as faceapi from '@vladmandic/face-api';
  import WebCam from 'components/WebCam.vue';
  import User from 'apis/user';

  const { proxy: ctx } = getCurrentInstance();
  const mainStore = useMainStore();

  let usernameRef = $ref(null);
  let webcam = $ref(null);
  let deviceId = $ref("");
  let cameras = $ref([]);
  let errorMessage = $ref("");
  let spinner = $ref(false);
  let loadedModels = $ref(false);

  async function onVideoLive() {
    // load model
    if (!loadedModels) await loadModels();
    const webcam = document.querySelector('#webcam');
    webcam.setAttribute('width', '100%');
    webcam.setAttribute('height', '100%');
    const canvasDom = document.querySelector('canvas');
    const canvas = faceapi.createCanvasFromMedia(webcam);
    const canvasSize = { width: webcam.clientWidth, height: webcam.clientHeight };
    faceapi.matchDimensions(canvas, canvasSize);
    // clear canvas
    if (canvasDom) document.querySelector('.overlay').removeChild(canvasDom);
    document.querySelector('.overlay').appendChild(canvas);
    // close spinner loading
    mainStore.setLoadingStatus(false);

    // face detection
    setInterval(async () => {
      const detections = await faceapi.detectAllFaces(
        webcam,
        new faceapi.TinyFaceDetectorOptions(),
      );
      const resizeDetections = faceapi.resizeResults(detections, canvasSize);
      canvas.getContext('2d').clearRect(0, 0, canvasSize.width, canvasSize.height);
      resizeDetections.forEach((detection) => {
        const score = Math.ceil(detection.score * 100) / 100;
        new faceapi.draw.DrawBox(
          {
            x: detection.box.x,
            y: detection.box.y,
            width: detection.box.width,
            height: detection.box.height,
          },
          { boxColor: score > 0.85 ? '#20c997' : '#6c757d' },
        ).draw(canvas);
        new faceapi.draw.DrawTextField([`${score}`], detection.box.bottomLeft, {
          backgroundColor: score > 0.85 ? '#20c997' : '#6c757d',
        }).draw(canvas);
      });
    }, 300);
  }

  async function onCapture() {
    const base64Array = [];
    const imageLength = 5;
    mainStore.setFeatures([]);
    if (usernameRef.value == "") {
      errorMessage = '请输入姓名';
      return null;
    }
    spinner = true;
    for (let i = 0; i < imageLength; i += 1) {
      base64Array.push(webcam.capture());
      if (i !== imageLength - 1) await delay(500);
    }
    const features = await Promise.all(
      base64Array.map((base64) => {
        const img = document.createElement('img');
        img.src = base64;
        return faceapi
          .detectSingleFace(img)
          .withFaceLandmarks()
          .withFaceDescriptor();
      }),
    );

    // features.forEach(item => {
    //   if (item) {

    //   }
    // });

    const featuresStringify = features.map((item) => {
      if (item) return item.descriptor;
      if (!item) {
        errorMessage = '提取特征失败，请重试';
        spinner = false;
      }
      return null;
    });

    // let users = {
    //   1: {
    //     displayName: 'Haleclipse',
    //     features: features.map((item) => {
    //       if (item) return item.descriptor;
    //       return null;
    //     })
    //   },
    // };


    let user = {
      UserName: usernameRef.value,
      FaceDescriptors: JSON.stringify(featuresStringify),
    };

    if (errorMessage == '') {
      await User.createUser(user);
    }

    // localStorage.setItem('users', JSON.stringify(users));

    errorMessage = featuresStringify.includes(null) ? '请重新提取特征' : '';
    spinner = false;
    mainStore.setFeatures(featuresStringify);
    mainStore.setModalStatus(true);
  }

  function onCameras(cameraList) {
    cameras = cameraList;
    deviceId = cameraList[0].deviceId;
  }

  function onCameraChange() {
    mainStore.setLoadingMsg('相机启动中');
    mainStore.setLoadingStatus(true);
  }

  function onError(error) {
    errorMessage = error;
    mainStore.setLoadingStatus(false);
  }

  async function loadModels() {
    mainStore.setLoadingMsg('模型载入中');
    await Promise.all([
      faceapi.nets.faceLandmark68Net.loadFromUri('/models'),
      faceapi.nets.faceRecognitionNet.loadFromUri('/models'),
      faceapi.nets.ssdMobilenetv1.loadFromUri('/models'),
      faceapi.nets.tinyFaceDetector.loadFromUri('/models'),
    ]);
    loadedModels = true;
    return Promise.resolve();
  }

  onMounted(() => {
    mainStore.setLoadingMsg('相机启动中');
    mainStore.setLoadingStatus(true);
  })
</script>

<style lang="scss" scoped>
</style>